﻿using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace UtilityToolsCollect.ObjectsLibrary.ProcessManage
{
    /// <summary>
    /// 性能计数器
    /// categoryName(性能对象名称：大的分类，如进程，如处理器等等)
    /// counterName(计数器名称：针对那一项数值的名称)
    /// instanceName(实例名称：进程实例的名称)
    /// 具体名称的查询，参照系统的【性能监视器->监视工具->性能监视器->[+]号(添加计数器)】窗口中的内容去查询
    /// 计数器对应counterName，实例对应instanceName  对象对应categoryName
    /// windows性能计数器程序名[Perfmon]
    /// LODCTR /R 可以重置系统计数器
    /// </summary>
    public class CpuPerformanceCount : IDisposable
    {
        public enum PerformanceCountTypeEnum
        {
            /// <summary>
            /// cpu占用-旧系统兼容
            /// </summary>
            ProcessorTime,
            /// <summary>
            /// cpu占用-旧系统兼容-频率变化倍率正比-当前频率÷基本频率=倍率-倍率越低(降频)-取值越小
            /// </summary>
            ProcessorTime_DirectlyProportional,
            /// <summary>
            /// cpu占用-旧系统兼容-频率变化倍率反比-当前频率÷基本频率=倍率-倍率越低(降频)-取值越大
            /// </summary>
            ProcessorTime_InverselyProportional,
            /// <summary>
            /// cpu占用-新版
            /// </summary>
            InformationProcessorTime,
            /// <summary>
            /// cpu占用-新版-频率变化倍率正比-当前频率÷基本频率=倍率-倍率越低(降频)-取值越小
            /// </summary>
            InformationProcessorTime_DirectlyProportional,
            /// <summary>
            /// cpu占用-新版-频率变化倍率反比-当前频率÷基本频率=倍率-倍率越低(降频)-取值越大
            /// </summary>
            InformationProcessorTime_InverselyProportional,
            /// <summary>
            /// cpu空闲-旧系统兼容
            /// </summary>
            Idle,
            /// <summary>
            /// cpu空闲-新版
            /// </summary>
            InformationIdle,
            /// <summary>
            /// 进程的cpu占用
            /// </summary>
            Process,
            /// <summary>
            /// 进程的cpu占用-频率变化倍率正比-当前频率÷基本频率=倍率-倍率越低(降频)-取值越小
            /// </summary>
            Process_DirectlyProportional,
            /// <summary>
            /// 进程的cpu占用-频率变化倍率反比-当前频率÷基本频率=倍率-倍率越低(降频)-取值越大
            /// </summary>
            Process_InverselyProportional,
            /// <summary>
            /// 任务管理器显示的值
            /// </summary>
            ProcessorUtility,
            /// <summary>
            /// 处理器性能-当前频率÷基本频率-结果是0-100的比值
            /// </summary>
            ProcessorPerformance
        }
        PerformanceCounter _PerformanceCounter;
        PerformanceCounter? _ProcessorPerformance;
        private static int LogicalProcessorCount;
        public PerformanceCountTypeEnum PerformanceCountType { get; private set; }
        private bool _InitialDelay = false;
        private Action<Exception>? ExceptionAction;

        private CpuPerformanceCount(PerformanceCountTypeEnum type, PerformanceCounter performanceCount, Action<Exception>? exceptionAction = null)
        {
            LogicalProcessorCount = Environment.ProcessorCount;
            PerformanceCountType = type;
            _PerformanceCounter = performanceCount;
            ExceptionAction = exceptionAction;
            if (type is PerformanceCountTypeEnum.Process_DirectlyProportional or PerformanceCountTypeEnum.Process_InverselyProportional)
                _ProcessorPerformance = new("Processor Information", "% Processor Performance", "_Total", true);
        }

        private CpuPerformanceCount(PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime, string? ProcessName = null, Action<Exception>? exceptionAction = null)
        {
            LogicalProcessorCount = Environment.ProcessorCount;
            PerformanceCountType = type;
            ExceptionAction = exceptionAction;
            _PerformanceCounter = type switch
            {
                PerformanceCountTypeEnum.ProcessorUtility => new("Processor Information", "% Processor Utility", "_Total", true),
                PerformanceCountTypeEnum.Idle => new("Processor", "% Idle Time", "_Total", true),
                PerformanceCountTypeEnum.InformationIdle => new("Processor Information", "% Idle Time", "_Total", true),

                PerformanceCountTypeEnum.ProcessorTime or PerformanceCountTypeEnum.ProcessorTime_DirectlyProportional or PerformanceCountTypeEnum.ProcessorTime_InverselyProportional
                    => new("Processor", "% Processor Time", "_Total", true),

                PerformanceCountTypeEnum.InformationProcessorTime or PerformanceCountTypeEnum.InformationProcessorTime_DirectlyProportional or PerformanceCountTypeEnum.InformationProcessorTime_InverselyProportional
                    => new("Processor Information", "% Processor Time", "_Total", true),


                PerformanceCountTypeEnum.ProcessorPerformance => new("Processor Information", "% Processor Performance", "_Total", true),

                PerformanceCountTypeEnum.Process or PerformanceCountTypeEnum.Process_DirectlyProportional or PerformanceCountTypeEnum.Process_InverselyProportional => string.IsNullOrEmpty(ProcessName)
                ? throw new ArgumentNullException("Process模式中，必须指定进程实例的完整名称")
                    : new("Process", "% Processor Time", ProcessName, true),

                _ => throw new ArgumentException("无效参数"),
            };

            if (type
            is PerformanceCountTypeEnum.ProcessorTime_DirectlyProportional or PerformanceCountTypeEnum.ProcessorTime_InverselyProportional
            or PerformanceCountTypeEnum.InformationProcessorTime_DirectlyProportional or PerformanceCountTypeEnum.InformationProcessorTime_InverselyProportional
            or PerformanceCountTypeEnum.Process_DirectlyProportional or PerformanceCountTypeEnum.Process_InverselyProportional)
            {
                _ProcessorPerformance = new("Processor Information", "% Processor Performance", "_Total", true);
                _ProcessorPerformance.NextValue();
            }
            _PerformanceCounter.NextValue();
        }

        private void initialDelay() { if (!_InitialDelay) { Thread.Sleep(100); _InitialDelay = true; } }
        private async Task initialDelayAsync() { if (_InitialDelay) await Task.CompletedTask; else { await Task.Delay(100); _InitialDelay = true; } }
        private float getValue()
        {
            try
            {
                return PerformanceCountType switch
                {
                    PerformanceCountTypeEnum.Process => _PerformanceCounter.NextValue() / LogicalProcessorCount,

                    PerformanceCountTypeEnum.Process_DirectlyProportional or PerformanceCountTypeEnum.InformationProcessorTime_DirectlyProportional or PerformanceCountTypeEnum.Process_DirectlyProportional
                        => _PerformanceCounter.NextValue() / LogicalProcessorCount * (_ProcessorPerformance!.NextValue() / 100),

                    PerformanceCountTypeEnum.ProcessorTime_InverselyProportional or PerformanceCountTypeEnum.InformationProcessorTime_InverselyProportional or PerformanceCountTypeEnum.Process_InverselyProportional
                        => _PerformanceCounter.NextValue() / LogicalProcessorCount / (_ProcessorPerformance!.NextValue() / 100),

                    _ => _PerformanceCounter.NextValue()
                };
            }
            catch (Exception ex)
            {
                ExceptionAction?.Invoke(ex);
                return float.NaN;
            }
        }

        public float Value { get { IfDisposed(); initialDelay(); return getValue(); } }
        public async Task<float> GetValueAsync()
        {
            IfDisposed();
            await initialDelayAsync();
            return await Task.Run(getValue);
        }



        private static bool CreateProcessPerformanceCounter(Process process, out PerformanceCounter counter)
        {
            counter = new PerformanceCounter("Process", "ID Process", true);
            foreach (string InstanceName in new PerformanceCounterCategory("Process").GetInstanceNames()
                    .Where(name => name.StartsWith(process.ProcessName, StringComparison.OrdinalIgnoreCase)))
            {
                counter.InstanceName = InstanceName;
                if (counter.RawValue == process.Id)
                    return true;
            }
            counter.Dispose();
            counter = null!;
            return false;
        }

        private static bool CreateProcessPerformanceCounter(string ProcessName, int ProcessID, out PerformanceCounter counter)
        {
            ProcessName = Path.GetFileNameWithoutExtension(ProcessName);
            counter = new PerformanceCounter("Process", "ID Process", true);
            foreach (string InstanceName in new PerformanceCounterCategory("Process").GetInstanceNames()
                    .Where(name => name.StartsWith(ProcessName, StringComparison.OrdinalIgnoreCase)))
            {
                counter.InstanceName = InstanceName;
                if (counter.RawValue == ProcessID)
                    return true;
            }
            counter.Dispose();
            counter = null!;
            return false;
        }

        private static bool CreateProcessPerformanceCounter(int ProcessID, out PerformanceCounter counter)
        {
            counter = new PerformanceCounter("Process", "ID Process", true);
            foreach (string InstanceName in new PerformanceCounterCategory("Process").GetInstanceNames())
            {
                counter.InstanceName = InstanceName;
                if (counter.RawValue == ProcessID)
                    return true;
            }
            counter.Dispose();
            counter = null!;
            return false;
        }

        public static bool TryCreate(out CpuPerformanceCount cpuPerformanceCount, Process? process = null, string? ProcessName = null, int ProcessID = -1, PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime, Action<Exception>? exceptionAction = null)
        {
            cpuPerformanceCount = null!;
            if (type is PerformanceCountTypeEnum.Process or PerformanceCountTypeEnum.Process_DirectlyProportional or PerformanceCountTypeEnum.Process_InverselyProportional)
            {
                PerformanceCounter counter;
                bool Result;

                if (process != null)
                    Result = CreateProcessPerformanceCounter(process, out counter);

                else if (!string.IsNullOrEmpty(ProcessName) && ProcessID >= 0)
                    Result = CreateProcessPerformanceCounter(ProcessName, ProcessID, out counter);

                else if (ProcessID >= 0)
                    Result = CreateProcessPerformanceCounter(ProcessID, out counter);
                else return false;

                if (Result)
                    cpuPerformanceCount = new(type, counter, exceptionAction);
                return Result;
            }
            cpuPerformanceCount = new(type, string.Empty, exceptionAction);
            return true;
        }

        private static CpuPerformanceCount Create(Process? process = null, string? ProcessName = null, int ProcessID = -1, PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime, Action<Exception>? exceptionAction = null)
        {
            if (TryCreate(out CpuPerformanceCount counter, process, ProcessName, ProcessID, type, exceptionAction))
                return counter;
            if (type is PerformanceCountTypeEnum.Process or PerformanceCountTypeEnum.Process_DirectlyProportional or PerformanceCountTypeEnum.Process_InverselyProportional)
                throw new InvalidOperationException($"创建失败:没有捕捉到有效进程! 进程参数:{{Name:{process?.ProcessName}|ID:{process?.Id}}}");
            throw new Exception("未知异常！");
        }


        public static CpuPerformanceCount Create(PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime, Action<Exception>? exceptionAction = null)
            => Create(null, null, -1, type, exceptionAction);
        public static async Task<CpuPerformanceCount> CreateAsync(PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime,  Action<Exception>? exceptionAction = null)
            => await Task.Run(() => Create(type, exceptionAction));


        public static CpuPerformanceCount Create(Process? process, PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime, Action<Exception>? exceptionAction = null)
            => Create(process, null, -1, type, exceptionAction);
        public static async Task<CpuPerformanceCount> CreateAsync(Process? process,PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime,  Action<Exception>? exceptionAction = null)
            => await Task.Run(() => Create(process, type, exceptionAction));


        public static CpuPerformanceCount Create(int ProcessID, string? ProcessName = null, PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime, Action<Exception>? exceptionAction = null)
                    => Create(null, ProcessName, ProcessID, type, exceptionAction);
        public static async Task<CpuPerformanceCount> CreateAsync(int ProcessID, string? ProcessName = null, PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime, Action<Exception>? exceptionAction = null)
            => await Task.Run(() => Create(ProcessID, ProcessName, type, exceptionAction));


        public static CpuPerformanceCount Create(int ProcessID, PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime, Action<Exception>? exceptionAction = null)
                    => Create(null, null, ProcessID, type, exceptionAction);
        public static async Task<CpuPerformanceCount> CreateAsync(int ProcessID, PerformanceCountTypeEnum type = PerformanceCountTypeEnum.ProcessorTime, Action<Exception>? exceptionAction = null)
            => await Task.Run(() => Create(ProcessID, type, exceptionAction));




        #region IDisposable Support
        private bool disposedValue = false;
        private void IfDisposed()
        {
            if (disposedValue)
                throw new ObjectDisposedException("对象已经释放");
        }
        protected virtual void OnDispose()
        {
            if (!disposedValue)
            {
                ExceptionAction = null;
                if (_ProcessorPerformance is not null)
                {
                    _ProcessorPerformance.Close();
                    _ProcessorPerformance.Dispose();
                    _ProcessorPerformance = null!;
                }
                _PerformanceCounter!.Close();
                _PerformanceCounter.Dispose();
                _PerformanceCounter = null!;
                disposedValue = true;
            }
        }
        ~CpuPerformanceCount() { OnDispose(); }
        public void Dispose() { OnDispose(); GC.SuppressFinalize(this); }
        #endregion


        [Serializable]
        public class CreateCpuPerformanceCountException : Exception
        {
            public CreateCpuPerformanceCountException() { }
            public CreateCpuPerformanceCountException(string message) : base(message) { }
            public CreateCpuPerformanceCountException(string message, Exception inner) : base(message, inner) { }
            [Obsolete] protected CreateCpuPerformanceCountException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
        }
    }

}
